/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package compilador_full;
import java.util.Hashtable;
/**
 *
 * @author jomas eu
 */
public class Compilador_nucleo {
    
    // cria o objeto das funções
    public Functions_compilator funcoes = new Functions_compilator();
    // grava as primitivas nas respectivas hash's
    
    // hash para os erros
    public Hashtable<Integer, String> savErros = new Hashtable<Integer, String>();
    
    // núcleo compilator
    public String Compilator (String codigo, TabelaSimbolos tabelasimbolos) {

        // Retorna a nova string
        String colorCodigo = "";
        // vars
        int i;
        // variável que define se ja existe ponto
        boolean ponto = false, numb = false;
        // var cod, stooped, Token
        String cod = "", token = "", last = "";

        // string de Estado
        // os estados serão : decisao; identificador; numero; comentario, erro, operador;
        // inicialmente em decisao
        String estado = "decisao";
        // vars para linhas e colunas
        int colunas = 1, linhas = 1;
            
        // maquina de estados principal
        for (i = 0; i < codigo.length(); i++) { //
            
            // define a variável cod
            cod = "";
            cod += codigo.charAt(i);
            
            // Q0
            // Estado em Paralelo
            // define o estado Inicial que é DECISAO
            if (estado.equals("decisao")) {
                // verifica primeiro se pertence a Gramática
                if (isGramatica(cod) ){
                    // verifica se Tende para um delimitador/keyword
                    if (funcoes.letra.containsKey(cod)) {
                        // atribui o estado
                        estado = "identificador";
                    // verifica se tende para um numero
                    } else if (funcoes.digito.containsKey(cod)) {
                        // atribui o estado
                        estado = "numero";
                    // verifica se é um operador / um delimitador
                    } else if (funcoes.operador.containsKey(cod) || cod.equals("!") || cod.equals("|") || funcoes.delimitador.containsKey(cod)) {
                        // atribui o estado
                        estado = "operadorEstop";
                    } else {
                        // joga qualquer coisa na saida, para considerar os espaços fins de linha, tabs
                        if (cod.charAt(0) == 9)
                            colorCodigo += "&nbsp;&nbsp;&nbsp;&nbsp;"; // trata o TAB como HTML
                        else
                            colorCodigo += cod;
                    }
                // Se não for um caracter válido vai para o erro
                } else {
                    // atribui o estado para erro
                    estado = "ErroIdNum";
                }
            // Q11 - Q12 - Q13
            // Comentário Núcleo //
            // verifica se os laços corresponden 
            } else if (estado.equals("comentario1")) {
                
                // verifica se existe o comentário
                if (cod.charAt(0) == 13 || cod.equals("\n")) {
                    // atribui a função de ficar azul
                    colorCodigo += funcoes.addBlue(token)+cod;
                    // apaga variavel token
                    token = "";
                    // volta para o estado decisão
                    estado = "decisao";
                } else {
                    // adiciona na variável token - Não grava token nessa etapa Consome qualquer coisa
                    token += cod;
                }
            // Q14 - Q15 - Q16
            // Comentário Núcleo /*
            // verifica se os laços corresponden 
            } else if (estado.equals("comentario2")) {
                // verifica se o token anterior (last) e o código atual são critérios de parada ou fim de arquivo
                if ((last.equals("*") && cod.equals("/")) || i == codigo.length()-1) {
                    // atribui a parada
                    // atribui a função de ficar azul
                    colorCodigo += funcoes.addBlue(token+cod);
                    // apaga variavel token
                    token = "";
                    // apaga last
                    last = "";
                    // volta para o estado decisão
                    estado = "decisao";
                    // verifica se não terminou, o código e não foi completador o */
                    //if (i != codigo.length()-1)
                        // salva na tabela de tokens
                       // sav_Tokens("*/", "comentario", linhas);
                    //else 
                    if (i == codigo.length()-1)
                        sav_Erros("*/ Experado", linhas, colunas);
                } else {
                    // adiciona na variável token - Não grava token nessa etapa Consome qualquer coisa
                    token += cod;
                    // atribui ao token anterior
                    last = cod;
                }
            }
            //
            
            // Q1
            // Identificador - Nucleo
            // PRocura ser é uma letra numero ou _
            if (estado.equals("identificador")) {
                // aguarda até o proximo criterio de parada
                if (isStopIdNum(cod)) {
                   // verifica se é uma palavra reservada
                    if (funcoes.palavra.containsKey(token))
                        // se for uma palavra reservada pinta de BOLD
                        colorCodigo += funcoes.addBold(token);
                    else 
                        // adiciona o token ao codigo novo e pinta de VERDE
                        colorCodigo += funcoes.addGreen(token);
                    
                    // salva na tabela de tokens
                    sav_Tokens(token, "letra", linhas, tabelasimbolos);
                    // zero a variavel token // todo final de token ele tem q ser zerado
                    token = "";
                    // Muda para o estado operador para verificar se existe operador ainda (duplo)
                    estado = "operadorEstop";
                // verifica se é uma letra, digito ou numero
                } else
                    if (funcoes.letra.containsKey(cod) || funcoes.digito.containsKey(cod) || cod.equals("_"))
                         // guarda o token
                        token += cod;
                    else
                        // mudo de estado ErroId
                        estado = "ErroIdNum";
            } 
            //
            
            // Q2 - Q3
            // Numero - Nucleo
            // verifica os laços correspondentes as condições de numero/digito
            if (estado.equals("numero")) {
                // aguarda até o proximo criterio de parada
                if (isStopIdNum(cod)) {
                    // verifica se tem ponto
                    if (cod.equals(".") && !ponto) {
                        // coloca ponto ON
                        ponto = true;
                        // coloca numero em falso
                        numb = false;
                        // guarda o token
                        token += cod;
                    } else if (ponto && numb || !(ponto && !numb)) {
                            // adiciona o token ao codigo novo e pinta de marrom
                            colorCodigo += funcoes.addPurple(token);
                            // salva na tabela de tokens
                            sav_Tokens(token, "numero", linhas, tabelasimbolos);
                            // zero a variavel token // todo final de token ele tem q ser zerado
                            token = "";
                            // atribui ponto zera = false e numb
                            ponto = false;
                            numb = false;
                            // Muda para o estado operador para verificar se existe operador ainda (duplo)
                            estado = "operadorEstop";
                        } else {
                            // mudo de estado Erronumero
                            estado = "ErroIdNum";
                            // atribui ponto zera = false e numb
                            ponto = false;
                            numb = false;
                        }
                // verifica se é um numero ou um ponto
                } else if (funcoes.digito.containsKey(cod)) {
                     // guarda o token
                    token += cod;
                    // se for numb coloca On
                    numb = true;
                } else {
                    // mudo de estado Erronumero
                    estado = "ErroIdNum";
                    // atribui ponto zera = false e numb
                    ponto = false;
                    numb = false;
                }
            }
            //
            
            // Erro Geral para Id e Numero
            // Verifica se há erro em id número ou algum caracter inválido
            if (estado.equals("ErroIdNum")) {
                // aguarda até o proximo critério de parada do Erro + as Exeções "!" e "|"
                if (isStopIdNum(cod)) {
                    // ERRO ! //
                    // transformo em vermelho quando Ã© erro      
                    colorCodigo += funcoes.addRed(token);
                    // salva na tabela de Erros
                    sav_Erros(token, linhas, colunas);
                    // zero a variavel token // todo final de token ele tem q ser zerado
                    token = "";
                    // Muda para o estado operador para verificar se existe operador ainda (duplo)
                    estado = "operadorEstop";
                // adiciona o caracter inválido
                } else
                    token += cod;
            }
            //   
            
            // Q4 até Q36
            // Define Os operadores e O critério de parada
            // Define os criétior de Parada
            if (estado.equals("operadorEstop")) {
                // verifica se o token é diferente de vazio
                if (!token.equals("")) {
                        // Q4
                        // indica que há uma decisão em relação a um token anterior
                        // verifica se pode ser um número negativo
                        if (token.equals("-") && funcoes.digito.containsKey(cod)) {
                            // atribui o estado
                            estado = "numero";
                            // grava o token
                            token += cod;
                        // Qx adicional ASPAS
                        // verifica se é um uma aspa dupla
                        } else if (token.equals("\"")) {
                            // atribui o estado aspas
                            estado = "aspas";
                        // Q11 - Q12
                        // verifica se é um OpDuplo se qualquer outra coisa
                        } else if (token.equals("/") && cod.equals("/")) {
                            // atribui o estado comentário //
                            estado = "comentario1";
                            // atribui o codigo ao token ticando com /*
                            token += cod;
                            // salva na tabela de tokens
                            //sav_Tokens(token, "comentario", linhas);
                        // Q11 - Q14
                        } else if (token.equals("/") && cod.equals("*")) {
                            // atribui o estado comentário //
                            estado = "comentario2";
                            // atribui o codigo ao token ticando com /*
                            token += cod;
                            // salva na tabela de tokens
                            //sav_Tokens(token, "comentario", linhas);
                        // Q22 - Q23
                        // verifica se é um !=
                        } else if (token.equals("!") && cod.equals("=")) {
                            // muda o estado para OpDuplo
                            estado = "OpDuplo";
                        // se encontrar um ! junto de qualquer outra coisa é erro
                        } else if (token.equals("!")) {
                           // estado vai para erro de Op
                           estado = "ErroOpD";
                        // Q31 - Q32
                        // verifica se é um ||
                        } else if (token.equals("|") && cod.equals("|")) {
                            // muda o estado para OpDuplo
                            estado = "OpDuplo";
                        // verifica o erro
                        } else if (token.equals("|")) {
                           // estado vai para erro de Op
                           estado = "ErroOpD";
                        // Q19 - Q20
                        // verifica se é um ==
                        } else if (token.equals("=") && cod.equals("=")) {
                            // muda o estado para OpDuplo
                            estado = "OpDuplo";
                        // Q25 - Q26 - Q27
                        // verifica se é um <=
                        } else if (token.equals("<") && cod.equals("=")) {
                            // muda o estado para OpDuplo
                            estado = "OpDuplo";
                        // Q25 - Q26 - Q27
                        // verifica se é um >=
                        } else if (token.equals(">") && cod.equals("=")) {
                            // muda o estado para OpDuplo
                            estado = "OpDuplo";
                        // Q8 - Q28 - Q29 - Q30
                        // verifica se é um &&
                        } else if (token.equals("&") && cod.equals("&")) {
                            // muda o estado para OpDuplo
                            estado = "OpDuplo";
                        // Q9 - Q10 - Q11
                        // verifica se é um ++
                        } else if (token.equals("+") && cod.equals("+")) {
                            // muda o estado para OpDuplo
                            estado = "OpDuplo";
                        // Q4 - Q5
                        // verifica se é um --
                        } else if (token.equals("-") && cod.equals("-")) {
                            // muda o estado para OpDuplo
                            estado = "OpDuplo";
                        // Q4 - Q7
                        // verifica se é um ->
                        } else if (token.equals("-") && cod.equals(">")) {
                            // muda o estado para OpDuplo
                            estado = "OpDuplo";
                        // ***
                        // separa entre os Duplos e Operadores Unicos
                        // ***  
                        // Q11
                        // verifica se é um +
                        } else if (token.equals("+")) {
                            // muda o estado para Unico
                            estado = "OpUnico";
                        // Q5
                        // verifica se é um -
                        } else if (token.equals("-")) {
                            // muda o estado para Unico
                            estado = "OpUnico";
                        // Q17 - Q18
                        // verifica se é um *
                        } else if (token.equals("*")) {
                            // muda o estado para Unico
                            estado = "OpUnico";
                        // Q25 - Q27
                        // verifica se é um >
                        } else if (token.equals(">")) {
                            // muda o estado para Unico
                            estado = "OpUnico";
                        // Q25 - Q27
                        // verifica se é um <
                        } else if (token.equals("<")) {
                            // muda o estado para Unico
                            estado = "OpUnico";
                        // Q19 - Q21
                        // verifica se é um =
                        } else if (token.equals("=")) {
                            // muda o estado para Unico
                            estado = "OpUnico";
                        // Q34 - Q35
                        // verifica se é um .
                        } else if (token.equals(".")) {
                            // muda o estado para Unico
                            estado = "OpUnico";
                        // Q28 - Q30
                        // verifica se é um &
                        } else if (token.equals("&")) {
                            // muda o estado para Unico
                            estado = "OpUnico";
                        // Q11
                        // verifica se é um /
                        } else if (token.equals("/")) {
                            // muda o estado para Unico
                            estado = "OpUnico";
                        } 
                // verifica se é um operador
                } else if (funcoes.operador.containsKey(cod) || cod.equals("!") || cod.equals("|")) {
                     // grava o token
                     token += cod;
                // Q35 - Q36
                // verifica se é um Delimitador        
                } else if (funcoes.delimitador.containsKey(cod)) {
                    // vai para o estado delimitador
                    estado = "delimitador";
                } else {
                    // Q para espaços, e outros caracteres
                    // joga qualquer coisa na saida, para considerar os espaços fins de linha, tabs
                    if (cod.charAt(0) == 9)
                        colorCodigo += "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"; // trata o TAB como HTML
                    else
                        colorCodigo += cod;
                    // volta para decisão
                    estado = "decisao";
                }
            }
            
            // Q8 - Q34
            // Estado para o OperadorUnico
            // Grava as condições de operador Yellow
            if (estado.equals("OpUnico")) {
                // salva na tabela de tokens
                sav_Tokens(token, "operador", linhas, tabelasimbolos);
                // verifica se é da gramática
                if(isGramatica(cod)) {
                    // verifica antes se é um espaço, \n, 13 (enter) ou 9 (tab)
                    if (cod.equals(" ") || cod.charAt(0) == 13 || cod.equals("\n") || cod.charAt(0) == 13 || cod.charAt(0) == 9) {
                        // grava na cor do token correspondente
                         if (token.equals("<")) 
                            colorCodigo += funcoes.addYellow("&lt;"+cod);
                        else
                            colorCodigo += funcoes.addYellow(token+cod);
                        // Muda o estado de acordo com o cod
                        estado = "decisao";
                        // zera o token
                        token = "";
                    } else {
                        // grava na cor do token correspondente
                        // salva na tabela de tokens
                        if (token.equals("<")) 
                            colorCodigo += funcoes.addYellow("&lt;");
                        else
                            colorCodigo += funcoes.addYellow(token);
                        // Muda o estado de acordo com o cod
                        estado = retornaEstado(cod);
                        // atribui o cod ao token
                        token = cod;
                    }
                } else {
                      // grava na cor do token correspondente
                         if (token.equals("<")) 
                            colorCodigo += funcoes.addYellow("&lt;");
                        else
                            colorCodigo += funcoes.addYellow(token);
                        // Muda o estado de acordo com o cod
                        estado = "ErroOpD";
                        // zera o token
                        token = "";
                }
            }
            //
            
            // Q8 - Q34
            // Estado para o Operador Duplo
            // Grava as condições de operador Yellow
            if (estado.equals("OpDuplo")) {
               // grava na cor do token correspondente
               // salva na tabela de tokens
               if (token.equals("<") && cod.equals("="))
                    colorCodigo += funcoes.addYellow("&lt;=");
               else
                    colorCodigo += funcoes.addYellow(token+cod);
               // salva na tabela de tokens
               sav_Tokens(token+cod, "operador", linhas, tabelasimbolos);
               // adicina a classe para
               estado = "decisao";
               // zera o token
               token = "";
            }
            //

            // adicional " "
            // fecha e abre aspas
            if (estado.equals("aspas")) {
               // System.out.printf(Integer.parseInt(cod)+"\n");
                // verifica se o token anterior (last) e o código atual são critérios de parada ou fim de arquivo
                if (cod.equals("\"")) {
                    // atribui a parada
                    // atribui a função de ficar azul
                    colorCodigo += funcoes.addYellow(token+cod);

                    sav_Tokens(token+cod, "frase", linhas, tabelasimbolos);

                    // apaga variavel token
                    token = "";
                    // volta para o estado decisão
                    estado = "decisao";
                } else if (i == codigo.length()-1){
                    // verifica se não fechou as aspas
                    sav_Erros("\" Experado", linhas, colunas);
                    colorCodigo += funcoes.addRed(token+cod);
                    token = "";
                    // volta para o estado decisão
                    estado = "decisao";
                } else {
                    // adiciona na variável token - Não grava token nessa etapa Consome qualquer coisa
                    token += cod;
                }
            }
            //
            
            // Erro Geral para Operador
            // Verifica se é um erro de operador
            if (estado.equals("ErroOpD")) {
                // verifica antes se é um espaço, \n, 13 (enter) ou 9 (tab)
                if (cod.equals(" ") || cod.charAt(0) == 13 || cod.equals("\n") || cod.charAt(0) == 13 || cod.charAt(0) == 9) {
                        // transformo em vermelho quando é erro      
                        colorCodigo += funcoes.addRed(token)+cod;
                        // salva na tabela de Erros
                        sav_Erros(token, linhas, colunas);
                        //
                        // Muda o estado de acordo com o cod
                        estado = "decisao";
                        // grava o token como o código
                        token = "";
                } else {
                  // aguarda até o proximo critério de parada do Erro + as Exeções "!" e "|"
                    if (funcoes.letra.containsKey(cod) || funcoes.digito.containsKey(cod) || funcoes.delimitador.containsKey(cod) ||
                            funcoes.operador.containsKey(cod))  {
                        // transformo em vermelho quando é erro      
                        colorCodigo += funcoes.addRed(token);
                        // salva na tabela de Erros
                        sav_Erros(token, linhas, colunas);
                        //
                        // Muda o estado de acordo com o cod
                        estado = retornaEstado(cod);
                        // grava o token como o código
                        token = cod;
                    // adiciona o caracter inválido
                    } else
                        token += cod;
                }
            }
            //
            
            // Q35 - Q36
            // Estado para o Delimitador
            // Grava as condições de operador Gay
            if (estado.equals("delimitador")) {
                // salva na tabela de tokens
                sav_Tokens(cod, "delimitador", linhas, tabelasimbolos);
                // adiciona no codigo colodito
                colorCodigo += funcoes.addGray(cod);
                // zera o token
                token = "";
                // volta para o estado de decisão
                estado = "decisao";
            }
            //
            
            // Em paralelo Contagem de linhas e colunas
            // conta as linhas e colunas
            if (i > 0 && (codigo.charAt(i) == 13 || codigo.equals("\n"))) {
                // incrementa linha
                linhas++;
                // zera colunas
                colunas = 0;
            } else
                // incrementa colunas
                colunas++;
            
        //
        } // Fim do FOR e da varredua dos estados.
        
        // Imprime colocando os <br> para o fim de linha
        return funcoes.printBr(colorCodigo);

    }
    
    // verifica se o código digitado pertence primeiramente a gramática
    // Para ser da gramatica ele tem que ser Letra, Digito, OPERADOR, Delimitador, Espaço, Fins de Linha e as Exceções "!" e "|"
    public boolean isGramatica (String cod) {
        // verifica se o caracter digitado é valido
        if (funcoes.operador.containsKey(cod) || funcoes.delimitador.containsKey(cod) ||
                funcoes.letra.containsKey(cod) || funcoes.digito.containsKey(cod) || 
                cod.equals(" ") || cod.charAt(0) == 13 || cod.equals("\n") ||
                cod.equals("!") || cod.equals("|") || cod.charAt(0) == 13 || cod.charAt(0) == 9)
            return true;
        else
            return false;
    }
    // Verifica a função de Stop para Identificador e Numero que pode ser um OPERADOR, DELIMITADOR, ESPAÇO, FINS DE LINHA
    public boolean isStopIdNum (String cod) {
        // verifica se é um espaço, operador, delimitador
        if (funcoes.operador.containsKey(cod) || funcoes.delimitador.containsKey(cod) ||
                cod.equals(" ") || cod.charAt(0) == 13 || cod.equals("\n") || cod.charAt(0) == 9 || cod.equals("!") || cod.equals("|"))
            return true;
        else
            return false;
    }
    // Tabela de Tokens
    // Variavel aux para contar a lista de tokens
    int sav = 0;
    // salva na tabela de tokens
    public void sav_Tokens (String token, String tipo, int linha, TabelaSimbolos tabelasimbolos) {
        // classifica o token antes
        if (tipo.equals("letra"))
            if (funcoes.palavra.containsKey(token))
                tabelasimbolos.insert(token, "palavra", linha);
            else
                tabelasimbolos.insert(token, "identificador", linha);
        else
           //
           tabelasimbolos.insert(token, tipo, linha);
    }
    // Variavel aux para contar a lista de tokens
    int savE = 0;
    // salva na tabela de tokens
    public void sav_Erros (String token, int linha, int coluna) {
        // verifica antes se o token ja existe
        if (!savErros.containsKey(token) && !(token.equals(" ") || token.equals("\n") || token.equals("") || token.equals(" ") || token.equals(13)))
            // salva na tabela de tokens
            savErros.put(savE++, token+"                                     <Erro Linha "+linha+" - Coluna "+coluna+">");
    }
    // me retorna o estado correspondente ao valor de entrada
    public String retornaEstado (String cod) {
        // retorna o estado correspondente
        if (funcoes.letra.containsKey(cod))
            return "identificador";
        else if (funcoes.digito.containsKey(cod))
            return "numero";
        else if (funcoes.delimitador.containsKey(cod))
            return "delimitador";
        else
            return "operadorEstop";
    }

}
// Fim do método